package ufpb;

import com.sun.org.apache.xerces.internal.jaxp.DocumentBuilderFactoryImpl;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.LinkedList;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import ufpb.compiladores.lexico.Automaton;
import ufpb.compiladores.lexico.AutomatonFactory;
import ufpb.compiladores.lexico.Enviroment;
import ufpb.compiladores.lexico.LexicalTable;
import ufpb.compiladores.lexico.Reader;
import ufpb.compiladores.lexico.XMLLoader;
import ufpb.compiladores.lexico.run.GUI.CallTables;
import ufpb.compiladores.lexico.run.GUI.SelectFiles;
import ufpb.compiladores.lexico.run.HtmlGenerator;
import ufpb.compiladores.sintatico.SyntacticalAnalyzer;
import ufpb.compiladores.sintatico.SyntacticalAnalyzerFactory;

/**
 *
 * @author seminick
 */
public class Run {

    private enum OutType {
        
        GUI,
        FILE,
        CONSOLE,
        HTML
    }
    
    public static void main(String[] args) {

        File confl = null;
        File confs = null;
        boolean lphase = true;
        boolean sphase = true;
        LinkedList<File> sources = new LinkedList<File>();
        File out = null;
        OutType ot = OutType.CONSOLE;
        
        for (String arg : args) {
            
            if (arg.matches("--lexical-conf=.+")) {
                
                String[] parts = arg.split("=");
                confl = new File(parts[1]);
            } else if(arg.matches("--syntactical-conf=.+")) {
                
                String[] parts = arg.split("=");
                confs = new File(parts[1]);
            } else if(arg.matches("--source=.+")) {
                
                String[] parts = arg.split("=");
                sources.add(new File(parts[1]));
            } else if(arg.matches("--out=.+")) {
                
                String[] parts = arg.split("=");
                out = new File(parts[1]);
            } else if(arg.matches("--outtype=.+")) {
                
                String[] parts = arg.split("=");
                if (parts[1].equals("GUI")) {
                    
                    ot = OutType.GUI;
                } else if (parts[1].equals("FILE")) {
                    
                    ot = OutType.FILE;
                } else if (parts[1].equals("CONSOLE")) {
                    
                    ot = OutType.CONSOLE;
                } else if (parts[1].equals("HTML")) {
                    
                    ot = OutType.HTML;
                } else {
                    
                    System.out.println("[ERROR] Unkown outtype value " + parts[1]);
                    usage();
                    System.exit(1);
                }
            } else if (arg.matches("--phases=.+")) {
                
                lphase = false;
                sphase = false;
                String[] phases = arg.split("[|]");
                for (String phase : phases) {
                    
                    phase = phase.trim();
                    if (phase.equalsIgnoreCase("LEXICAL")) {
                        
                        lphase = true;
                    } else if (phase.equalsIgnoreCase("SYNTACTICAL")) {
                        
                        sphase = true;
                    }
                }
            } else {
                 
                System.out.println("[WARNING] Ignoring unkown parameter: " + arg);
            }
        }
        
        if (sphase && !lphase) {
            
            System.out.println("[ERROR] Syntactical phase needs lexical phase");
            usage();
            System.exit(1);
        }
        
        if (confs == null) {
            
            System.out.println("[ERROR] Missing syntactical configuration file");
            usage();
            System.exit(1);
        } else if (confl == null) {
            
            System.out.println("[ERROR] Missing lexical configuration file");
            usage();
            System.exit(1);
        } else if (ot == OutType.FILE && out == null) {
            
            System.out.println("[ERROR] You must specify a out file when saving output to a file."
                    + " or use --outtype=GUI");
            usage();
            System.exit(1);
        } else if (ot == OutType.HTML && out == null) {
            
            System.out.println("[ERROR] You must specify a out file when saving output to HTML."
                    + " or use --outtype=GUI");
            usage();
            System.exit(1);
        }
        
        if (lphase) {
            
            System.out.println("Creating lexical enviroment with: " + confl.getAbsolutePath());
            try {

                XMLLoader load = new XMLLoader(confl);
                Enviroment env = load.createEnviroment();
                ufpb.compiladores.sintatico.Enviroment senv = null;
                if (sphase) {

                    System.out.println("Creating syntactical enviroment with: " + confs.getAbsolutePath());
                    ufpb.compiladores.sintatico.XMLLoader sload = 
                            new ufpb.compiladores.sintatico.XMLLoader(confs);
                    senv = sload.createEnviroment();
                }

                /*if (ot == OutType.GUI) {

                    if (sources.size() == 0) {

                        (new SelectFiles()).setVisible(true);
                    } else {

                        File[] sf = sources.toArray(new File[0]);
                        Document[] docs = new Document[sf.length];
                        for (int i = 0; i < sf.length; i++) {

                            Automaton a = AutomatonFactory.getAutomaton(env, new Reader(sf[i]));
                            //docs[i] = a.generateTable();
                            a.generateTable(docs[i], docs[i].createElement("Root"));
                        }
                        (new CallTables(docs, sf)).setVisible(true);
                    }
                } else {*/

                Document outdoc = DocumentBuilderFactoryImpl.newInstance()
                        .newDocumentBuilder().newDocument();
                Element r = outdoc.createElement("Root");
                Document[] docss = new Document[sources.size()];
                SyntacticalAnalyzer[] ana = new SyntacticalAnalyzer[sources.size()];
                for (int f = 0; f < sources.size(); f++) {

                    File source = sources.get(f);
                    if (source.exists()) {

                        try {

                            LexicalTable lt = null;
                            Reader sr = new Reader(source);
                            Automaton auto = AutomatonFactory.getAutomaton(env, sr);
                            docss[f] = DocumentBuilderFactoryImpl.newInstance()
                                            .newDocumentBuilder().newDocument();
                            //lt = auto.generateTable(outdoc, r);
                            Element rfile = docss[f].createElement("File");
                            rfile.setAttribute("file", source.getAbsolutePath());
                            Element lentry = docss[f].createElement("Lexical");
                            lt = auto.generateTable(docss[f], lentry);
                            rfile.appendChild(lentry);
                            
                            sr.close();

                            if (sphase) {

                                Element sentry = docss[f].createElement("Syntactical");
                                
                                senv.removeRecursion();
                                senv.removeNonDeterminism();

                                SyntacticalAnalyzer sa = SyntacticalAnalyzerFactory
                                        .createSyntacticalAnalyzer(senv, lt);

                                sa.processEnviroment(docss[f], sentry);
                                /*JFrame j = new JFrame();
                                j.getContentPane().add(sa.getSyntaticalTree().getComponent(),
                                        BorderLayout.CENTER);
                                j.setSize(640,480);
                                j.setVisible(true);
                                j.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);*/
                                rfile.appendChild(sentry);

                            }
                            
                            //outdoc.appendChild(rfile.cloneNode(true));
                            //outdoc.adoptNode(rfile.cloneNode(true));
                            Node in = outdoc.importNode(rfile, true);
                            r.appendChild(in);
                        } catch (IOException ioe) {

                            System.out.println("[ERROR] IO Error when processing "
                                    + source.getAbsolutePath());
                        }
                    } else {

                        System.out.println("[WARNING] File not found: " +  source.getAbsolutePath());
                    }
                   // }
                    
                   // System.exit(0);
                }
                
                Transformer tf = TransformerFactory.newInstance().newTransformer();
                //tf.setOutputProperty(OutputKeys., null);
                tf.setOutputProperty(OutputKeys.INDENT, "yes");
                DOMSource docs = new DOMSource(outdoc);
                switch (ot) {

                    case FILE:

                        //StringWriter sw = new StringWriter();
                        FileOutputStream outs = new FileOutputStream(out);
                        StreamResult srr = new StreamResult(outs);
                        tf.transform(docs, srr);
                        outs.flush();
                        outs.close();
                        break;
                    case CONSOLE:

                        //System.out.println(outdoc.toString());
                        StreamResult srrc = new StreamResult(System.out);
                        tf.transform(docs, srrc);
                        break;

                    case HTML:

                        //TODO Gerar HTML para múltiplos arquivos
                        String html = HtmlGenerator.generateHtml(outdoc, sources.get(0));
                        FileOutputStream fos = new FileOutputStream(out);
                        fos.write(html.getBytes());
                        fos.flush();
                        fos.close();
                        break;

                    case GUI:

                        if (lphase && !sphase) {

                            if (sources.size() > 0) {

                                (new CallTables(docss,
                                        sources.toArray(new File[docss.length]), ana))
                                        .setVisible(true);
                            } else {

                                (new CallTables(false)).setVisible(true);
                            }
                        } else if (lphase && sphase) {

                            if (sources.size() > 0) {

                                (new CallTables(docss,
                                        sources.toArray(new File[docss.length]), ana))
                                        .setVisible(true);
                            } else {

                                (new CallTables(true)).setVisible(true);
                            }
                        }
                        break;
                }

            } catch (Exception e) {

                System.out.println("[ERROR] An error occured with msg:");
                System.out.println(e.getMessage());
                System.out.println(e.getClass());
                e.printStackTrace();
                //e.printStackTrace();
                System.exit(1);
            }
        }
    }
    
    private static void usage() {
        
        System.out.println("[INFO]");
        System.out.println("Projeto de compiladores UFPB 2011.2");
        System.out.println("Developers: José Ivan, Sarah Soares");
        System.out.println("--lexical-conf=FILE Lexical configuration file");
        System.out.println("--syntactical-conf=FILE Syntactical configuration file");
        System.out.println("--out=FILE File to put output if --outtype=FILE");
        System.out.println("--outtype=FILE|GUI|CONSOLE|HTML Output type. CONSOLE is default");
        System.out.println("--source=FILE Source file to be processed. Multiple files can be passed.");
        System.out.println("--source=FILE1 --source=FILE2 --source=FILE3");
        System.out.println("--phases=LEXICAL|SYNTACTICAL");
    }
}
